#include "hs_logger.h"
#include <chrono>

#ifdef HS_USE_CXX_STANDARD_11
#include "hs_c11ext.h"
#endif

hs::Logger::Logger()
    : enableInfo(true)
    , enableLevelPrefix(true)
    , enableTimePrefix(false)
    , enableSimpleLevelPrefix(true)
    , enableOnlyToFile(false)
    , floatPrecisionNum(0)
{
}

hs::Logger::~Logger()
{
    close();
}

void hs::Logger::send(LogLevelType level, const std::ostringstream & os)
{
    std::string time_str;
    timePrefix(time_str);

    std::string msg;
    msg += time_str;
    msg += levelPrefix(level);
    msg += os.str();
    if (level == LogLevelType::DEBUG) {
        debug(msg);
    }
    else if (level == LogLevelType::INFO) {
        info(msg);
    }
    else if (level == LogLevelType::WARN) {
        warn(msg);
    }
    else if (level == LogLevelType::ERROR) {
        error(msg);
    }
    else if (level == LogLevelType::FATAL) {
        fatal(msg);
    }
    else {
    }
}

void hs::Logger::send(LogLevelType level, const std::string & str)
{
    std::string time_str;
    timePrefix(time_str);

    std::string msg;
    msg += time_str;
    msg += levelPrefix(level);
    msg += str;
    if (level == LogLevelType::DEBUG) {
        debug(msg);
    }
    else if (level == LogLevelType::INFO) {
        info(msg);
    }
    else if (level == LogLevelType::WARN) {
        warn(msg);
    }
    else if (level == LogLevelType::ERROR) {
        error(msg);
    }
    else if (level == LogLevelType::FATAL) {
        fatal(msg);
    }
    else {
    }
}

void hs::Logger::send(const std::string & str)
{
    if (!enableOnlyToFile) {
    #if defined USE_MEX
        matlab::data::ArrayFactory factory;
        m_matlab->feval(u"fprintf", 0, std::vector<matlab::data::Array>( 
            { factory.createScalar(str) } ) );
    #else
        std::cout << str;
    #endif
    }
    if (m_log_file.is_open()) {
        m_log_file << str;
    }   
}

void hs::Logger::open(const std::string & log_path, bool enableAppend)
{
    close();
    if (enableAppend) {
        m_log_file.open(log_path, std::ios::out | std::ios::app);
    }
    else {
        m_log_file.open(log_path, std::ios::out | std::ios::trunc);
    }
}

void hs::Logger::close()
{
    if (m_log_file.is_open()) {
        m_log_file.close();
    }
}

void hs::Logger::setprecision(int num)
{
    floatPrecisionNum = 0 ? num <= 0 : num;
}

void hs::Logger::debug(const std::string & msg)
{
#ifndef NDEBUG
    if (!enableOnlyToFile) {
    #if defined USE_MEX
        matlab::data::ArrayFactory factory;
        m_matlab->feval(u"fprintf", 0, std::vector<matlab::data::Array>( 
            { factory.createScalar(msg) } ) );
    #else
        std::cout << msg;
    #endif
    }
    if (m_log_file.is_open()) {
        m_log_file << msg;
    }
#endif
}

void hs::Logger::info(const std::string & msg)
{
    if (enableInfo) {
        if (!enableOnlyToFile) {
        #if defined USE_MEX
            matlab::data::ArrayFactory factory;
            m_matlab->feval(u"fprintf", 0, std::vector<matlab::data::Array>( 
                { factory.createScalar(msg) } ) );
        #else
            std::cout << msg;
        #endif
        }
        if (m_log_file.is_open()) {
            m_log_file << msg;
        }   
    }
}

void hs::Logger::warn(const std::string & msg)
{
    if (!enableOnlyToFile) {
    #if defined USE_MEX
        matlab::data::ArrayFactory factory;
        m_matlab->feval(u"warning", 0, std::vector<matlab::data::Array>( 
            { factory.createScalar(msg) } ) );
    #else
        std::cout << msg;
    #endif
    }
    if (m_log_file.is_open()) {
        m_log_file << msg;
    }
}

void hs::Logger::error(const std::string & msg)
{
    if (!enableOnlyToFile) {
    #if defined USE_MEX
        matlab::data::ArrayFactory factory;
        m_matlab->feval(u"warning", 0, std::vector<matlab::data::Array>( 
            { factory.createScalar(msg) } ) );
    #else
        std::cout << msg;
    #endif
    }
    if (m_log_file.is_open()) {
        m_log_file << msg;
    }
}

void hs::Logger::fatal(const std::string & msg)
{
    if (m_log_file.is_open()) {
        m_log_file << msg;
    }
    if (!enableOnlyToFile) {
    #if defined USE_MEX
        matlab::data::ArrayFactory factory;
        m_matlab->feval(u"error", 0, std::vector<matlab::data::Array>( 
            { factory.createScalar(msg) } ) );
    #else
        std::cout << msg;
        std::exit(-1);
    #endif
    }
}

const char * hs::Logger::levelPrefix(LogLevelType level)
 {
    if (enableLevelPrefix && !enableSimpleLevelPrefix) {
        if (level == LogLevelType::DEBUG) {
            return "[DEBUG] ";
        }
        else if (level == LogLevelType::INFO) {
            return "[INFO ] ";
        }
        else if (level == LogLevelType::WARN) {
            return "[WARN ] ";
        }
        else if (level == LogLevelType::ERROR) {
            return "[ERROR] ";
        }
        else if (level == LogLevelType::FATAL) {
            return "[FATAL] ";
        }
        else {
            return "";
        }
    }

    if (enableLevelPrefix && enableSimpleLevelPrefix) {
        if (level == LogLevelType::DEBUG) {
            return "[-] ";
        }
        else if (level == LogLevelType::INFO) {
            return "[+] ";
        }
        else if (level == LogLevelType::WARN) {
            return "[!] ";
        }
        else if (level == LogLevelType::ERROR) {
            return "[?] ";
        }
        else if (level == LogLevelType::FATAL) {
            return "[X] ";
        }
        else {
            return "";
        }
    }

    return "";
}

void hs::Logger::timePrefix(std::string & time_str)
{
    if (enableTimePrefix) {
        // 获取当前系统时间
        std::chrono::system_clock::time_point now =  std::chrono::system_clock::now();
        time_t now_time_t = std::chrono::system_clock::to_time_t(now);
        
        // 转化为tm结构体
        std::tm now_tm;
        #ifdef _WIN32
        localtime_s(&now_tm, &now_time_t);
        #else
        localtime_r(&now_time_t, &now_tm);
        #endif

        // 格式化时间字符串
        std::stringstream ss;
        #ifdef HS_USE_CXX_STANDARD_11
        ss << "[" << hs::c11ext::put_time(&now_tm, "%H:%M:%S");
        #else
        ss << '[' << std::put_time(&now_tm, "%H:%M:%S");
        #endif
        // 获取毫秒部分
        std::chrono::system_clock::duration duration = now.time_since_epoch();
        std::chrono::milliseconds milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(duration);
        ss << '.' << std::setw(3) << std::setfill('0') << milliseconds.count() % 1000 << ']';

        time_str = ss.str();
    }
    else {
        time_str = "";
    }
}

hs::Logger _global_logger;
